package us.kg.kotoriforest.spring.aop.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

/**
 * 日志切面类
 *
 * @author Kotori rotroutine@163.com
 * @since 2025/1/27
 */
@Aspect
@Component
// @Order(1) 用于指定切面的优先级 数值越小优先级越高 切面的默认顺序是按照类名的字母顺序排序
public class LogAspect {
    // 切入点表达式 需要定位到接口的方法上 不能定位到实现类的方法上
    // * 通配符 表示任意字符
    // .. 在参数列表中表示任意个数的参数 在方法名中表示任意个数的包
    // && 切入点表达式之间可以使用&& || ! 进行逻辑运算
    // execution([modifier] returnType [declaringType] methodName(paramType) [throws Exception]) []表示可选
    // within() 用于匹配指定包下的所有类 within(us.kg.kotoriforest.spring.aop.calculator.*) 匹配us.kg.kotoriforest.spring.aop.calculator包下的所有类
    // args() 用于匹配方法参数 args(int, int) 匹配两个int类型的参数 args(java.io.Serializable) 匹配一个实现了Serializable接口的参数
    // @args() 用于匹配方法参数的运行时类型 @args() 匹配参数的运行时类型上有指定注解的参数
    // @annotation() 用于匹配方法上的注解 @annotation() 匹配方法上有指定注解的方法
    // bean() 用于匹配指定的bean bean("beanName") 匹配指定名称的bean bean(*Service) 匹配所有以Service结尾的bean
    // AOP代理分为两种情况：被代理对象有接口 被代理对象没有接口
    // 如果被代理对象有接口 则Spring使用JDK动态代理 如果被代理对象没有接口 则Spring使用CGLIB动态代理
    // JDK动态代理的实现是兄弟模式 实现相同的接口
    // CGLIB动态代理的实现是子类模式 继承被代理对象
    @Before("pointcut()")
    public void before() {
        System.out.println("before...");
    }

    @After("pointcut()")
    public void after(JoinPoint joinPoint) {
        // System.out.println(joinPoint.getSignature().toLongString());
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        System.out.println(signature.getDeclaringTypeName() + "." + signature.getName());
    }

    @AfterReturning(value = "pointcut()", returning = "result")
    public void afterReturning(Object result) {
        System.out.println("afterReturning...");
    }

    @AfterThrowing(value = "pointcut()", throwing = "ex")
    public void afterThrowing(Throwable ex) {
        System.out.println("afterThrowing...");
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Object result = pjp.proceed();
        return result;
    }

    @Pointcut("execution(* us.kg.kotoriforest.spring.aop.calculator.*.*(..))")
    private void pointcut() {
    }
}
